/* 
 * File:   SymbolTable.cpp
 * Author: Kamil Chaber
 */

#include "symboltable.h"

SymbolTable::SymbolTable() {
    this->currAddress = 0;
    
    this->initReservedValues();
}

void SymbolTable::initReservedValues() {
    SymbolTableRecord writeProc("write", RECORD_RESERVED);
    this->elements.push_back(writeProc);
    SymbolTableRecord readProc("read", RECORD_RESERVED);
    this->elements.push_back(readProc);
    SymbolTableRecord exitProc("exit", RECORD_RESERVED);
    this->elements.push_back(exitProc);
}

int SymbolTable::insertIdentifier(string identifier) {
    //TODO: check reserved words
    SymbolTableRecord rec(identifier);
    vector<SymbolTableRecord>::iterator it = find(this->elements.begin(), this->elements.end(), rec);
    int index = distance(this->elements.begin(), it);
    if (index == this->elements.size()) {
        this->elements.push_back(rec);
        return this->elements.size() - 1;
    } else {
        return index;
    }
}

int SymbolTable::insertNumber(string number) {
    SymbolTableRecord rec(number);
    this->elements.push_back(rec);
    return this->elements.size() - 1;
}

int SymbolTable::insertRecord(SymbolTableRecord& rec) {
    this->elements.push_back(rec);
    return this->elements.size() - 1;
}

SymbolTableRecord& SymbolTable::getElement(int index) {
    return this->elements[index];
}

SymbolTableRecord& SymbolTable::getProgramElement() {
    vector<SymbolTableRecord>::iterator it = find_if(this->elements.begin(), this->elements.end(), IsProgramRecord);
    int index = distance(this->elements.begin(), it);
    return this->elements[index];
    //TODO: throw exception when program not found
}

int SymbolTable::getLastTempVariableIndex() {
    vector<SymbolTableRecord>::reverse_iterator rit = find_if(this->elements.rbegin(), this->elements.rend(), IsTemporaryVariable);
    int index = distance(this->elements.begin(), rit.base() - 1);
    
    if (index == this->elements.size()) {
        return EMPTY_INT_VALUE;
    }
    return index;
}

vector<SymbolTableRecord>& SymbolTable::getElements() {
    return this->elements;
}

string SymbolTable::allocateAddress(int index) {
    SymbolTableRecord& rec = this->getElement(index);
    
    if (rec.address.empty()) {
        stringstream currAddressString;
        currAddressString << this->currAddress;
        rec.address = currAddressString.str();
        
        switch (rec.type) {
            case RECORD_TYPE_TEMP_VARIABLE:
            case RECORD_TYPE_VARIABLE: {
                switch (rec.varValueType) {
                    case VAR_VALUE_TYPE_INTEGER: {
                        this->currAddress += INT_MEMORY_SIZE;
                        break;
                    }
                    case VAR_VALUE_TYPE_REAL: {
                        this->currAddress += REAL_MEMORY_SIZE;
                        break;
                    }
                }
                break;
            }
        }
    } else {
        cout << "Address already set for the element on index: " << index << endl;
    }
    return rec.address;
}

void SymbolTable::setUpElementType(int varIndex, SymbolTableElementType type) {
    SymbolTableRecord& rec = this->elements[varIndex];
    rec.type = type;
}

string SymbolTable::toString() {
    stringstream strBuilder;
    strBuilder << "SymbolTable [\n";
    for (int i = 0; i < this->elements.size(); i++) {
        SymbolTableRecord rec = this->elements[i];
        strBuilder << "\t" << i << "\t" << rec.toString() + "\n";
    }
    strBuilder << "]\n";

    string result = strBuilder.str();
    return result;
}

SymbolTable::~SymbolTable() {
}

